package xaj.meta.visit;

import java.util.ArrayList;
import java.util.List;

import polyglot.ast.NodeFactory;
import polyglot.frontend.Job;
import polyglot.types.TypeSystem;
import polyglot.util.TypedList;
import xaj.scc.ast.ClosureFactor;
import xaj.scc.ast.ConcatFactor;
import xaj.scc.ast.Factor;
import xaj.scc.ast.LambdaFactor;
import xaj.scc.ast.NamedFactor;
import xaj.scc.ast.OptionalFactor;
import xaj.scc.ast.OrFactor;
import xaj.scc.ast.Production;
import xaj.scc.ast.ProductionElement;
import xaj.scc.ast.SCNodeFactory;
import xaj.scc.ast.SimpleFactor;
import xaj.scc.ast.SyntaxClassDecl;
import xaj.scc.ast.SyntaxDeclarationInsertion;

public class Visitor_Dudu_01 extends XAJVisitor {

	private int n = 0;
	private String nameOfSyntax;
	private List<Production> novasProducoes;
	private List<Factor> insideNamedFactor;
	private List<NamedFactor> namedFactor;
	private List<ProductionElement> productionsElementsOfOrFactor;

	private List<ProductionElement> productionsElementsOfOptionalFactor;
	private List<ProductionElement> productionsElementsOfClosureFactor;

	public Visitor_Dudu_01(Job job, TypeSystem typeSystem,
			NodeFactory nodeFactory) {
		super(job, typeSystem, nodeFactory);
		novasProducoes = new ArrayList<Production>();
		insideNamedFactor = new ArrayList<Factor>();
		namedFactor = new ArrayList<NamedFactor>();
		productionsElementsOfOrFactor = new ArrayList<ProductionElement>();
		productionsElementsOfOptionalFactor = new ArrayList<ProductionElement>();
		productionsElementsOfClosureFactor = new ArrayList<ProductionElement>();

	}

	public void visitOrFactor(OrFactor of) {
		if (getVisitType() == VisitType.LEAVE) {
			SCNodeFactory nf = (SCNodeFactory) nodeFactory();
			String s = getNameOfNonTerminal();
			List<ProductionElement> list = new ArrayList<ProductionElement>();
			ProductionElement p1 = nf.ProductionElement(of.position(), of
					.getLeftSide(), nf.SemanticAction(null, null));
			ProductionElement p2 = nf.ProductionElement(of.position(), of
					.getRightSide(), nf.SemanticAction(null, null));
			list.add(p1);
			list.add(p2);
			productionsElementsOfOrFactor.addAll(list);
			novasProducoes.add(nf.Production(of.position(), s, list));
			SimpleFactor sif = nf.SimpleFactor(of.position(), s);
			for (Factor n : insideNamedFactor) {
				if (n == of) {
					changeNode(sif);
					return;
				}
			}
			NamedFactor named = nf.NamedFactor(of.position(), nomeDaVariavel(),
					sif);
			insideNamedFactor.add(sif);
			changeNode(named);

		}
	}

	public void visitOptionalFactor(OptionalFactor of) {
		if (getVisitType() == VisitType.LEAVE) {
			SCNodeFactory nf = (SCNodeFactory) nodeFactory();
			String s = getNameOfNonTerminal();
			List<ProductionElement> list = new ArrayList<ProductionElement>();
			ProductionElement p = nf.ProductionElement(of.position(), of
					.getFactor(), nf.SemanticAction(null, null));
			list.add(p);
			LambdaFactor l = nf.LambdaFactor(of.position());

			ProductionElement p2 = nf.ProductionElement(of.position(), l, nf
					.SemanticAction(null, null));

			list.add(p2); // add lambda

			productionsElementsOfOptionalFactor.addAll(list);
			novasProducoes.add(nf.Production(of.position(), s, list));
			SimpleFactor sif = nf.SimpleFactor(of.position(), s);

			for (Factor n : insideNamedFactor) {
				if (n == of) {
					changeNode(sif);
					return;
				}
			}
			NamedFactor named = nf.NamedFactor(of.position(), nomeDaVariavel(),
					sif);
			insideNamedFactor.add(sif);
			changeNode(named);
		}
	}

	public void visitClosureFactor(ClosureFactor cf) {
		if (getVisitType() == VisitType.LEAVE) {
			SCNodeFactory nf = (SCNodeFactory) nodeFactory();
			String s = getNameOfNonTerminal();
			List<ProductionElement> list = new ArrayList<ProductionElement>();
			ConcatFactor conctf = nf.ConcatFactor(cf.position(), nf
					.NamedFactor(cf.position(), nomeDaVariavel(), nf
							.SimpleFactor(cf.position(), s)), cf.getFactor());
			ProductionElement p = nf.ProductionElement(cf.position(), conctf,
					nf.SemanticAction(null, null));
			list.add(p);
			LambdaFactor l = nf.LambdaFactor(cf.position());
			ProductionElement p2 = nf.ProductionElement(cf.position(), l, nf
					.SemanticAction(null, null));
			list.add(p2); // add lambda
			productionsElementsOfClosureFactor.addAll(list);
			novasProducoes.add(nf.Production(cf.position(), s, list));
			SimpleFactor sif = nf.SimpleFactor(cf.position(), s);
			for (Factor n : insideNamedFactor) {
				if (n == cf) {
					changeNode(sif);
					return;
				}
			}
			NamedFactor named = nf.NamedFactor(cf.position(), nomeDaVariavel(),
					sif);
			insideNamedFactor.add(sif);
			changeNode(named);
		}

	}

	public void visitNamedFactor(NamedFactor namf) {
		if (getVisitType() == VisitType.LEAVE) {
			insideNamedFactor.add(namf.getFactor());
			getNamedFactor().add(namf);
		}
	}

	public void visitSyntaxClassDecl(SyntaxClassDecl scd) {
		if (getVisitType() == VisitType.ENTER) {
			nameOfSyntax = scd.name();
			n = 0;
			novasProducoes.clear();
		}

	}

	public void visitSyntaxDeclarationInsertion(SyntaxDeclarationInsertion sdi) {
		if (getVisitType() == VisitType.LEAVE) {
			SyntaxDeclarationInsertion sd = (SyntaxDeclarationInsertion) sdi
					.copy();
			TypedList tl = new TypedList(new ArrayList(), Production.class,
					false);
			for (Production p : sdi.getProductions()) {
				tl.add(p);
			}
			for (Production p : novasProducoes) {
				tl.add(p);
			}
			sd = sdi.setProductions(tl);
			changeNode(sd);
		}
	}

	public String getNameOfNonTerminal() {
		n++;
		return nameOfSyntax + "_aux_" + n;
	}

	private String nomeDaVariavel() {

		return "aux_" + n;
	}

	/**
	 * @param namedFactor
	 *            the namedFactor to set
	 */
	public void setNamedFactor(List<NamedFactor> namedFactor) {
		this.namedFactor = namedFactor;
	}

	/**
	 * @return the namedFactor
	 */
	public List<NamedFactor> getNamedFactor() {
		return namedFactor;
	}

	private String getNewMap() {
		return "MapImpl";
	}

	public List<ProductionElement> getProductionsElementsOfOrFactor() {
		return productionsElementsOfOrFactor;
	}

	public void setProductionsElementsOfOrFactor(
			List<ProductionElement> productionsElementsOfOrFactor) {
		this.productionsElementsOfOrFactor = productionsElementsOfOrFactor;
	}

	public List<ProductionElement> getProductionsElementsOfOptionalFactor() {
		return productionsElementsOfOptionalFactor;
	}

	public void setProductionsElementsOfOptionalFactor(
			List<ProductionElement> productionsElementsOfOptionalFactor) {
		this.productionsElementsOfOptionalFactor = productionsElementsOfOptionalFactor;
	}

	public List<ProductionElement> getProductionsElementsOfClosureFactor() {
		return productionsElementsOfClosureFactor;
	}

	public void setProductionsElementsOfClosureFactor(
			List<ProductionElement> productionsElementsOfClosureFactor) {
		this.productionsElementsOfClosureFactor = productionsElementsOfClosureFactor;
	}

}
